package com.sysolve.betterCity.service;

import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import com.sysolve.betterCity.dao.HibernateDAO;
import com.sysolve.betterCity.model.Account;
import com.sysolve.betterCity.model.Activity;
import com.sysolve.betterCity.model.ActivityType;
import com.sysolve.betterCity.model.Person;
import com.sysolve.betterCity.model.Place;
import com.sysolve.common.HibernateSessionFactory;

public class BetterCityService {
	public static BetterCityService instance = new BetterCityService();
	public static BetterCityService getInstance() {
		return instance;
	}
	
	public static int TRUE = 1;
	public static int FALSE = 0;
	public static int RADIUS = 500;
	
	HibernateDAO personDAO = new HibernateDAO() {public Class getModelClass() {return Person.class;} };
	HibernateDAO accountDAO = new HibernateDAO() {public Class getModelClass() {return Account.class;} };
	HibernateDAO activityDAO = new HibernateDAO() {public Class getModelClass() {return Activity.class;} };
	HibernateDAO placeDAO = new HibernateDAO() {public Class getModelClass() {return Place.class;} };
	HibernateDAO activityTypeDAO = new HibernateDAO() {public Class getModelClass() {return ActivityType.class;} };
	
	public Session getSession() {
		return HibernateSessionFactory.getSession();
	}
	
	public Account findAccount(String domain, String userId) {
		return (Account)accountDAO.getCriteria()
			.add(Restrictions.eq("domain", domain))
			.add(Restrictions.eq("userId", userId))
			.uniqueResult();
	}

	public Person findPerson(Account account) {
		if (account!=null)
			return account.getOwner();
		else
			return null;
	}
	
	public List<Person> findPersons(String domain, String userIds) {
		List<Account> accounts = accountDAO.getCriteria()
			.add(Restrictions.eq("domain", domain))
			.add(Restrictions.in("userId", userIds.split(",")))
			.list();
		List<Person> persons = new java.util.ArrayList<Person>();
		for (Account a:accounts) {
			if (a.getOwner()!=null)
				persons.add(a.getOwner());
		}
		return persons;
	}
	
	public Person createPerson(String domain, String userId, String username) {
		Account account = findAccount(domain, userId);
		if (account==null)
			account = new Account(domain, userId);
		if (findPerson(account)==null) {
			Person p = new Person(username);
			personDAO.save(p);
			account.setOwner(p);
		} else {
			account.getOwner().setUsername(username);
			personDAO.save(account.getOwner());
		}
		accountDAO.save(account);
		return account.getOwner();
	}
	
	public List<Place> findPlaceNearBy(Person person, int lat, int lng, int maxRecords) {
		Criteria c = placeDAO.getCriteria().add(Restrictions.or(Restrictions.eq("isPublic", TRUE), Restrictions.eq("owner", person)));
		c.add(Restrictions.between("lat", lat-RADIUS, lat+RADIUS));
		c.add(Restrictions.between("lng", lng-RADIUS, lng+RADIUS));
		return c.setMaxResults(maxRecords).list();
	}
	
	public List<Place> findNewPlace(Person person, int fromPlaceId, int maxRecords) {
		Criteria c = placeDAO.getCriteria().add(Restrictions.or(Restrictions.eq("isPublic", TRUE), Restrictions.eq("owner", person)));
		return c.add(Restrictions.gt("id", fromPlaceId)).setMaxResults(maxRecords).list();
	}
	
	public List<Activity> findActivityByPersons(List<Person> persons, int maxRecords) {
		if (persons==null || persons.isEmpty()) return null;
		
		return activityDAO.getCriteria().add(Restrictions.in("owner", persons)).addOrder(Order.desc("eventTime")).setMaxResults(maxRecords).list();
	}
	
	public List<Activity> findActivityByPersons(List<Person> persons, ActivityType activityType, Date d1, Date d2, int maxRecords) {
		if (persons==null || persons.isEmpty()) return null;
		
		Criteria c = activityDAO.getCriteria().add(Restrictions.in("owner", persons));
		if (activityType!=null)
			c.add(Restrictions.eq("activityType", activityType));
		if (d1!=null)
			c.add(Restrictions.ge("eventTime", d1));
		if (d2!=null)
			c.add(Restrictions.le("eventTime", d2));
		return c.addOrder(Order.desc("eventTime")).setMaxResults(maxRecords).list();
	}
	
	public boolean createActivity(Activity activity) {
		Place place = activity.getPlace();
		if (place.getId()>0) {
			place = (Place)placeDAO.get(place.getId());
		} else {
			placeDAO.save(place);
		}
		activity.setPlace(place);
		activity.setActivityType((ActivityType)activityTypeDAO.get(activity.getActivityType().getId()));
		activityDAO.save(activity);
		
		return true;
	}
	
	public List<ActivityType> listActivityType() {
		return activityTypeDAO.getList();
	}
	
	public List<Object[]> listAllVaildPlace() {
		return placeDAO.getQuery("select a.place.id,a.place.lat,a.place.lng,count(Activity) from Activity as a where a.place.lat<>0 and a.place.lng<>0 group by a.place.id,a.place.lat,a.place.lng").list();
	}
}
